Editor.Connection = function (uuidShapeIni, uuidShapeEnd, uuidConnection) {

    this.uuidShapeIni = uuidShapeIni;
    this.uuidShapeEnd = uuidShapeEnd;

    if (uuidConnection == undefined) {
        this.uuid = Math.uuid();
    } else {
        this.uuid = uuidConnection;
    }

    this._shapeIni = Editor.STATE.SHAPES.get(this.uuidShapeIni);
    this._shapeEnd = Editor.STATE.SHAPES.get(this.uuidShapeEnd);
    this._typeConnection;
    this._figure;
    this._isDeleted = false;
    this._x1;
    this._y1;
    this._x2;
    this._y2;
}

Editor.Connection.prototype.getUuidShapeIni = function () {
    return this.uuidShapeIni;
}

Editor.Connection.prototype.getUuidShapeEnd = function () {
    return this.uuidShapeEnd;
}

Editor.Connection.prototype._findTypeConnection = function () {
    var typeShapeIni = this._shapeIni.getTypeClass();
    var typeShapeEnd = this._shapeEnd.getTypeClass();

    if (typeShapeIni == "vocabClass" && typeShapeEnd == "vocabClass") {
        this._typeConnection = "subClass";
    } else if (typeShapeIni == "vocabProperty" && typeShapeEnd == "vocabProperty") {
        this._typeConnection = "subProperty";
    } else if (typeShapeIni == "vocabIndividual" && typeShapeEnd == "vocabClass") {
        this._typeConnection = "type";
    } else if (typeShapeIni == "vocabProperty" && typeShapeEnd == "vocabClass") {
        this._typeConnection = "range";
    } else if (typeShapeIni == "property") {
        this._typeConnection = "tripleEnd";
    } else if (typeShapeEnd == "property") {
        this._typeConnection = "tripleIni";
    } else {
        this._typeConnection = "unknown";
    }
}

Editor.Connection.prototype._findPoints = function () {
    var posIniX = this._shapeIni.getX() + this._shapeIni.getWidth() / 2;
    var posIniY = this._shapeIni.getY() + this._shapeIni.getHeight() / 2;

    var posEndX = this._shapeEnd.getX() + this._shapeEnd.getWidth() / 2;
    var posEndY = this._shapeEnd.getY() + this._shapeEnd.getHeight() / 2;

    var pointIni = Editor.Geometry.intersectionShapeAndSegment(this._shapeIni, posIniX, posIniY, posEndX, posEndY);
    if (!pointIni) {
        pointIni = new Editor.Geometry.Vector(posIniX, posIniY)
    }
    var pointEnd = Editor.Geometry.intersectionShapeAndSegment(this._shapeEnd, posEndX, posEndY, posIniX, posIniY);
    if (!pointEnd) {
        pointEnd = new Editor.Geometry.Vector(posEndX, posEndY)
    }


    this._x1 = pointIni.x;
    this._y1 = pointIni.y;
    this._x2 = pointEnd.x;
    this._y2 = pointEnd.y;


}

Editor.Connection.prototype.click = function () {
    var conn = this.conn;
    if (conn._typeConnection != 'tripleIni' && conn._typeConnection != 'tripleEnd') {
        var cmd = new JS.Command({
            execute: function () {
                conn.remove();
            },
            undo: function () {
                conn.unremove();
            },
            stack: Editor.STATE.CMDSTACK
        });
        cmd.name = 'remove connection';
        cmd.execute();
    }
}

Editor.Connection.prototype.mouseover = function () {
    var conn = this.conn;
    if (conn._typeConnection != 'tripleIni' && conn._typeConnection != 'tripleEnd') {
        conn._figure.attr(Editor.CONFIG.CONNECTOR_OVER_ATTR);
        if (conn._figureArrowPoint) {
            conn._figureArrowPoint.attr(Editor.CONFIG.CONNECTOR_OVER_ATTR);
        }
        if (conn._figureArrowPointIni) {
            conn._figureArrowPointIni.attr(Editor.CONFIG.CONNECTOR_OVER_ATTR);
        }
    }
}

Editor.Connection.prototype.mouseout = function () {
    var conn = this.conn;
    if (conn && conn._typeConnection != 'tripleIni' && conn._typeConnection != 'tripleEnd') {
        conn._figure.attr(Editor.CONFIG.CONNECTOR_ATTR);
        if (conn._figureArrowPoint) {
            conn._figureArrowPoint.attr(Editor.CONFIG.CONNECTOR_ATTR);
        }
        if (conn._figureArrowPointIni) {
            conn._figureArrowPointIni.attr(Editor.CONFIG.CONNECTOR_ATTR);
        }
    	if (conn._typeConnection == 'range') {
        	conn._figure.attr(Editor.CONFIG.FIGCONNECTION_RANGE_ATTR);
    	}
    }
}

Editor.Connection.prototype.draw = function () {
    if (!this._figure) {
        this._findTypeConnection();
        this._findPoints();

        this._figure = Editor.STATE.CANVAS.path(["M", this._x1, this._y1, "L", this._x2, this._y2].join(",")).attr(Editor.CONFIG.CONNECTOR_ATTR);
        this._figure.conn = this;
        this._figure.click(this.click);
        this._figure.mouseover(this.mouseover);
        this._figure.mouseout(this.mouseout);

        if (this._typeConnection == "subClass" || this._typeConnection == "subProperty") {
            // http://taitems.tumblr.com/post/549973287/drawing-arrows-in-raphaeljs
            var size = 5;
            var angle = Math.atan2(this._x1 - this._x2, this._y2 - this._y1);
            angle = (angle / (2 * Math.PI)) * 360;
            this._figureArrowPoint = Editor.STATE.CANVAS.path("M" + this._x2 + " " + this._y2 + " L" + (this._x2 - size * 3) + " " + (this._y2 - size) + " L" + (this._x2 - size * 3) + " " + (this._y2 + size) + " L" + this._x2 + " " + this._y2).rotate((90 + angle), this._x2, this._y2).attr(Editor.CONFIG.CONNECTOR_ATTR).attr("fill", "white");
            this._figureArrowPoint.conn = this;
            this._figureArrowPoint.click(this.click);
            this._figureArrowPoint.mouseover(this.mouseover);
            this._figureArrowPoint.mouseout(this.mouseout);
        } else if (this._typeConnection == "tripleEnd") {
            this._figureArrowPoint = Editor.STATE.CANVAS.circle(this._x2, this._y2, Editor.CONFIG.FIGARROWPOINT_RADIUS).attr(Editor.CONFIG.FIGARROWPOINT_ATTR);
            this._figureArrowPoint.conn = this;
            this._figureArrowPoint.click(this.click);
            this._figureArrowPoint.mouseover(this.mouseover);
            this._figureArrowPoint.mouseout(this.mouseout);
        }
    	if (this._typeConnection == 'range') {
        	this._figure.attr(Editor.CONFIG.FIGCONNECTION_RANGE_ATTR);
            	this._figureArrowPoint = Editor.STATE.CANVAS.circle(this._x2, this._y2, Editor.CONFIG.FIGARROWPOINT_RANGE_RADIUS).attr(Editor.CONFIG.FIGARROWPOINT_ATTR);
            	this._figureArrowPoint.conn = this;
            	this._figureArrowPoint.click(this.click);
            	this._figureArrowPoint.mouseover(this.mouseover);
            	this._figureArrowPoint.mouseout(this.mouseout);

            	this._figureArrowPointIni = Editor.STATE.CANVAS.circle(this._x1, this._y1, Editor.CONFIG.FIGARROWPOINT_RANGE_RADIUS).attr(Editor.CONFIG.FIGARROWPOINT_ATTR);
            	this._figureArrowPointIni.conn = this;
            	this._figureArrowPointIni.click(this.click);
            	this._figureArrowPointIni.mouseover(this.mouseover);
            	this._figureArrowPointIni.mouseout(this.mouseout);
    	}
    } else {
        this._findPoints();
        this._figure.attr({
            path: ["M", this._x1, this._y1, "L", this._x2, this._y2].join(",")
        });
        if (this._typeConnection == "subClass" || this._typeConnection == "subProperty") {
            var size = 5;
            var angle = Math.atan2(this._x1 - this._x2, this._y2 - this._y1);
            angle = (angle / (2 * Math.PI)) * 360;
            this._figureArrowPoint.attr({
                path: "M" + this._x2 + " " + this._y2 + " L" + (this._x2 - size * 3) + " " + (this._y2 - size) + " L" + (this._x2 - size * 3) + " " + (this._y2 + size) + " L" + this._x2 + " " + this._y2
            }).rotate((90 + angle), this._x2, this._y2);
        } else if (this._typeConnection == "tripleEnd") {
            this._figureArrowPoint.attr({
                cx: this._x2,
                cy: this._y2
            });
        } else if (this._typeConnection == 'range') {
	    this._figureArrowPoint.attr({
                cx: this._x2,
                cy: this._y2
            });
	    this._figureArrowPointIni.attr({
                cx: this._x1,
                cy: this._y1
            });
	}
    }

}

Editor.Connection.prototype.hide = function () {
    if (this._figureArrowPoint) {
        this._figureArrowPoint.attr(Editor.CONFIG.INVISIBLE_ATTR);
    }
    if (this._figureArrowPointIni) {
        this._figureArrowPointIni.attr(Editor.CONFIG.INVISIBLE_ATTR);
    }
    this._figure.attr(Editor.CONFIG.INVISIBLE_ATTR);

}

Editor.Connection.prototype.show = function () {
    if (this._figureArrowPoint) {
        this._figureArrowPoint.attr(Editor.CONFIG.VISIBLE_ATTR);
        this._figureArrowPoint.toFront();
    }
    if (this._figureArrowPointIni) {
        this._figureArrowPointIni.attr(Editor.CONFIG.VISIBLE_ATTR);
        this._figureArrowPointIni.toFront();
    }
    this._figure.attr(Editor.CONFIG.VISIBLE_ATTR);
    this._figure.insertAfter(Editor.STATE.BACKGROUND);
    if (this._typeConnection == 'range') {
        this._figure.toFront();
        this._figure.attr(Editor.CONFIG.FIGCONNECTION_RANGE_ATTR);
    }
}

Editor.Connection.prototype.remove = function () {
    if (this._figureArrowPoint) {
        this._figureArrowPoint.remove();
        delete this._figureArrowPoint;
    }
    if (this._figureArrowPointIni) {
        this._figureArrowPointIni.remove();
        delete this._figureArrowPointIni;
    }
    this._figure.remove();
    delete this._figure;
    this._isDeleted = true;
}

Editor.Connection.prototype.unremove = function () {
    this._isDeleted = false;
    this.draw();
}

Editor.Connection.prototype.getUuid = function () {
    return this.uuid;
}

Editor.Connection.prototype.getIsDeleted = function () {
    return this._isDeleted;
}

Editor.Connection.prototype.getIsTriple = function () {
    return (this._typeConnection == "tripleIni" || this._typeConnection == "tripleEnd");
}

Editor.Connection.prototype.hasShape = function (uuidShape) {
    return (this.uuidShapeIni == uuidShape || this.uuidShapeEnd == uuidShape);
}

Editor.Connection.prototype.toObject = function () {
    return {
        'type': 'connection',
        'uuidIni': this.uuidShapeIni,
        'uuidEnd': this.uuidShapeEnd,
        'uuid': this.uuid
    }
}

Editor.Connection.prototype.getType = function () {
    return this._typeConnection;
}
